﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.IO;
using Fcdbas.Core.Net ;
using FileTransferProtocol;
using Fcdbas.Core.Util ;
using System.Threading ;

namespace FileTransferClient
{

    class FileDownLoadClient : FileTransferClient
    {
        public static string LogPath;
        #region Constructor

        private object LockStartProcess = new object();
        private Timer checkDownLoad;
        private int IdleCheck = 60000;
        public FileDownLoadClient(OnEventDelegate eventDelegate, TransferHead head)
            : base(eventDelegate, head)
        {
            LogPath = AppDomain.CurrentDomain.BaseDirectory + "DownLoadLog";
            checkDownLoad = new Timer(new TimerCallback(CheckDownLoadStatus));
            checkDownLoad.Change(IdleCheck, IdleCheck);
        }

        #endregion

        #region Methods
        /// <summary>
        /// 重新连接下载
        /// </summary>
        /// <param name="state"></param>
        private void CheckDownLoadStatus(object state)
        {
            bool bComplate = true;
            foreach (FileBlock block in Transfer.Blocks)
            {
                if (!block.Complated)
                {
                    if (block.FStream != null)
                    {
                        block.FStream.Close();
                        block.FStream = null;
                    }
                    block.StartProcess = false;
                    if (Connections.ContainsKey(block.ConnectionID))
                        SendReadyCommand(Connections[block.ConnectionID]);
                    bComplate = false;
                }
            }
            if (bComplate)
            {
                if(!File.Exists(Transfer.FileInfo.File))
                {
                    WriteToFile();
                }
                OnAllFinished(this.Transfer.FileInfo.MD5);
                OnTransferSum();
            }
        }
        private void SendBeginDownLoad(ISocketConnection Connection)
        {
            lock (LockStartProcess)
            {
                foreach (FileBlock block in Transfer.Blocks)
                {
                    lock (LockStartProcess)
                    {
                        if (!block.StartProcess)
                        {
                            block.StartProcess = true;
                            Fcdbas.Core.Log.XTrace.WriteLine(string.Format("开始下载，连接ID为:{0},块号为 : {1}", Connection.ConnectionId, block.BlockId), LogPath);
                            block.ConnectionID = Connection.ConnectionId;
                            NetSession Session = new NetSession(ConnectionState.Connected);
                            Session.CmdType = CommandType.DownBegin;
                            Session.FileBlock = block;
                            byte[] buffer = ObjectHelper.SerialzieObject<NetSession>(Session);
                            Connection.BeginSend(buffer);
                            Connection.BeginReceive();
                            // ThreadExecute(Session, e);
                            break;
                        }
                    }
                }
            }
           
        }
        #region OnConnected
        private void SendReadyCommand(ISocketConnection Connection)
        {
            if (Connection == null)
                return;
            NetSession Session = new NetSession(ConnectionState.Connected);
            Session.CmdType = CommandType.DownReady;
            byte[] send = ObjectHelper.SerialzieObject<NetSession>(Session);

            Connection.BeginSend(send);
            Connection.BeginReceive();
        }
        public void StartDownLoad(TransferHead tHead)
        {
            this.Transfer = tHead;
            bWriteToFile = false;
            checkDownLoad.Change(IdleCheck, IdleCheck);
            List<ISocketConnection> a = Connections.Values.AsEnumerable<ISocketConnection>().ToList<ISocketConnection>();
            
            for(int i = 0; i < a.Count ; i++)
            {
                SendReadyCommand(a[i]);
                Thread.Sleep(500);
            }
        }
        public override void OnConnected(ConnectionEventArgs e)
        {

            StringBuilder s = new StringBuilder();

            s.Append("------------------------------------------------" + "\r\n");
            s.Append("Connected - " + e.Connection.ConnectionId + "\r\n");
            s.Append(e.Connection.Host.HostType.ToString() + "\r\n");
            s.Append(e.Connection.Creator.Name + "\r\n");
            s.Append(e.Connection.Creator.EncryptType.ToString() + "\r\n");
            s.Append(e.Connection.Creator.CompressionType.ToString() + "\r\n");
            s.Append("------------------------------------------------" + "\r\n");

          //  Event(s.ToString());
            if (!Connections.ContainsKey(e.Connection.ConnectionId))
                Connections.Add(e.Connection.ConnectionId, e.Connection);
            s.Length = 0;
            SendReadyCommand(e.Connection);
            //
            //e.Connection.BeginReceive();
            //Thread.Sleep(123);

            //if (e.Connection.Host.HostType == HostType.htServer)
            //{
            //    e.Connection.BeginReceive();
            //}
            //else
            //{
            //    byte[] b = GetMessage(e.Connection.SocketHandle.ToInt32());
            //    e.Connection.BeginSend(b);
            //}

            
        }
        
        #endregion

        #region OnSent
        
        public override void OnSent(MessageEventArgs e)
        {

            if (!e.SentByServer)
            {
                StringBuilder s = new StringBuilder();

                s.Append("------------------------------------------------" + "\r\n");
                s.Append("Sent - " + e.Connection.ConnectionId + "\r\n");
                s.Append("Sent Bytes - " + e.Connection.WriteBytes.ToString() + "\r\n");
                s.Append("------------------------------------------------" + "\r\n");

               // Event(s.ToString().Trim());

                s.Length = 0;

            }
            

            //if (e.Connection.Host.HostType == HostType.htServer)
            //{
    
            //    if (!e.SentByServer)
            //    {
            //        e.Connection.BeginReceive();
            //    }

            //}
            //else
            //{
            //    e.Connection.BeginReceive();
            //}

        }
        
        #endregion

        #region OnReceived
        private DateTime recDt = DateTime.Now;
        public override void OnReceived(MessageEventArgs e)
        {
            if (Stop)
            {
              //  e.Connection.BeginDisconnect();
                return;
            }
            //StringBuilder s = new StringBuilder();

            //s.Append(DateTime.Now .ToString() + "\r\n");
            //s.Append("Received - " + e.Connection.ConnectionId + "\r\n");
            //s.Append("Received Bytes - " + e.Buffer.Length.ToString() + "\r\n");
            //s.Append("------------------------------------------------" + "\r\n");
       //     Console.WriteLine( s.ToString());
            NetSession msg = null;
            try
            {
                msg = ObjectHelper.DeserialzieObject<NetSession>(e.Buffer);
               // byte[] sendBuffer = null;
                if (msg == null)
                    return;
                checkDownLoad.Change(IdleCheck, IdleCheck);
                switch(msg.CmdType)
                {
                    case CommandType.DownTransfer:
                        TimeSpan ts = DateTime.Now - recDt;
                        recDt = DateTime.Now;
                    //    Console.WriteLine("rec time:" + ts.TotalMilliseconds.ToString());
                        //ProcessReceive(msg.FileBlock);
                        WriteToTempFile(msg.FileBlock);
                       // msg.CmdType = CommandType.DownSucess;
                       // msg.FileBlock = null;
                       // sendBuffer = ObjectHelper.SerialzieObject<NetSession>(msg);
                       // e.Connection.BeginSend(sendBuffer);
                    break;
                    case CommandType.DownReady:
                    SendBeginDownLoad(e.Connection);
                    break;
                    case CommandType.DownEnd:
                        if(!bWriteToFile)
                            WriteToFile(msg.FileBlock);
                        SendBeginDownLoad(e.Connection);
                    break;
                }
                
            }
            catch (Exception ex)
            {
                Fcdbas.Core.Log.XTrace.WriteException(ex, LogPath);
                //e.Connection.BeginReceive();
                Console.WriteLine(ex.Message);
                return;
            }
            e.Connection.BeginReceive();
           // e.Connection.BeginReceive();
            //SleepRandom(e.Connection.Host.HostType);

            //if (e.Connection.Host.HostType == HostType.htServer)
            //{
            //    e.Connection.BeginSend(e.Buffer);
            //}
            //else
            //{
            //    byte[] b = GetMessage(e.Connection.SocketHandle.ToInt32());
            //    e.Connection.BeginSend(b);
            //}

        }
        private void WriteToTempFile(FileBlock block)
        {

            FileBlock fb = Transfer.Blocks.Find(p => p.BlockId == block.BlockId);
            if (fb == null)
                return;
            if (fb.FStream == null)
            {
                fb.FStream = new FileStream(fb.TempFile, FileMode.Create, FileAccess.ReadWrite);
            }
            //FileHelper.WriteBuffToFile(block.Data, (int)fb.HasTransfer,block.CurrentSize , fb.TempFile, "", false);
            fb.FStream.Position = fb.HasTransfer;
            fb.FStream.Write(block.Data, 0, block.CurrentSize);
            fb.FStream.Flush();
            fb.HasTransfer = block.HasTransfer;
            fb.Complated = block.Complated;
           
            if (fb.Complated)
            {
                fb.FStream.Close();
                fb.FStream = null;
            }
            ProcessReceive(block);
        }
        private bool bWriteToFile = false;
        private void WriteToFile(FileBlock block)
        {
            WriteToTempFile(block);
            Thread.Sleep(50);
            
            bool complated = true;
            foreach (FileBlock b in Transfer.Blocks)
            {
                if (!b.Complated)
                {
                    complated = false;
                    break;
                }
            }
            if (!complated)
                return;
            if (bWriteToFile)
                return;
            //while (bWriteToFile)
            //{
            //    Thread.Sleep(100);
            //}

            bWriteToFile = true;

            WriteToFile();
            Thread.Sleep(100);
            OnAllFinished(this.Transfer.FileInfo.MD5);
            OnTransferSum();
            checkDownLoad.Change(Timeout.Infinite, IdleCheck);
            //bWriteToFile = false;

        }
        private void WriteToFile()
        {
            int ReadSize = 10240;
            var fbs = Transfer.Blocks.OrderBy(p => p.BeginPos);
            int writeOffset = 0;
            try
            {
                FileStream fwrite = new FileStream(Transfer.FileInfo.FilePath + "\\" + Transfer.FileInfo.FileName, FileMode.Create, FileAccess.Write);
                ///合并文件
                foreach (FileBlock fb in fbs)
                {
                    FileStream fs = new FileStream(fb.TempFile, FileMode.Open, FileAccess.Read);

                    byte[] data = new byte[ReadSize];
                    int readoffset = 0;
                    int read = 0;
                    read = fs.Read(data, 0, ReadSize);
                    while (read > 0 && readoffset < fb.TotalSize)
                    {
                        fwrite.Position = writeOffset;
                        fwrite.Write(data, 0, read);
                        readoffset += read;
                        writeOffset += read;
                        fs.Position = readoffset;
                        read = fs.Read(data, 0, ReadSize);
                    }
                    if (read > 0)
                    {
                        fwrite.Write(data, 0, read);
                        writeOffset += read;
                    }
                    fs.Close();
                }
                fwrite.Flush();
                fwrite.Close();
                if (Transfer.Blocks.Count > 0)
                {
                    string path = Fcdbas.Core.Util.FileHelper.GetFileDir(Transfer.Blocks[0].TempFile);
                    Directory.Delete(path, true);
                }
            }
            catch (Exception ex)
            {
                Fcdbas.Core.Log.XTrace.WriteException(ex);
            }
        }
        private int nCount = 0;
        private void ProcessReceive(FileBlock fb)
        {
            if (!ComplatedDic.Keys.Contains(fb.BlockId))
                return;
            ComplatedDic[fb.BlockId].HasTransfer = fb.HasTransfer;
            ComplatedDic[fb.BlockId].Complated  = fb.Complated;
            ComplatedDic[fb.BlockId].TotalSize = fb.TotalSize;
            nCount++;
            if (nCount == 20)
            {
                OnTransferSum();
                nCount = 0;
            }
            
        }
        #endregion

        #region OnDisconnected
        
        public override void OnDisconnected(ConnectionEventArgs e)
        {

            StringBuilder s = new StringBuilder();

            s.Append("------------------------------------------------" + "\r\n");
            s.Append("Disconnected - " + e.Connection.ConnectionId + "\r\n");
            s.Append("------------------------------------------------" + "\r\n");
            if (!Connections.ContainsKey(e.Connection.ConnectionId))
                Connections.Remove(e.Connection.ConnectionId);
           // Event(s.ToString());
            s.Length = 0;
            FileBlock fb = Transfer.Blocks.Find(p => p.ConnectionID == e.Connection.ConnectionId);
            if (fb != null)
            {
                if (!fb.Complated)
                    fb.StartProcess = false;
            }
            if (e.Connection != null)
            {
                e.Connection.AsClientConnection().BeginReconnect();
            }
            //if (e.Connection.Host.HostType == HostType.htServer)
            //{
            //    //------
            //}
            //else
            //{
            //    e.Connection.AsClientConnection().BeginReconnect();
            //}

        }
        
        #endregion

        #region OnException
        
        public override void OnException(ExceptionEventArgs e)
        {

            StringBuilder s = new StringBuilder();

            s.Append("------------------------------------------------" + "\r\n");
            s.Append("Exception - " + e.Exception.GetType().ToString() + "\r\n");
            s.Append("Exception Message - " + e.Exception.Message + "\r\n");

            if (e.Exception is ReconnectAttemptException)
            {
                s.Append("Attempted   - " + ((ReconnectAttemptException)e.Exception).Attempt.ToString() + "\r\n");
                s.Append("Max Reached - " + ((ReconnectAttemptException)e.Exception).MaxReached.ToString() + "\r\n");
                s.Append("------------------------------------------------" + "\r\n");
                //s.Append("Inner Error - " + ((SocketException)e.Exception.InnerException).ErrorCode.ToString() + "\r\n");
                s.Append("------------------------------------------------" + "\r\n");
               // s.Append("Creator - " + ((ReconnectAttemptException)e.Exception).Creator.Name + "\r\n");
               // s.Append("Creator - " + ((ReconnectAttemptException)e.Exception).Creator.EncryptType.ToString() + "\r\n");
               // s.Append("Creator - " + ((ReconnectAttemptException)e.Exception).Creator.CompressionType.ToString() + "\r\n");

            }

            if (e.Exception is SocketException)
            {
                s.Append("Socket Error - " + ((SocketException)e.Exception).ErrorCode.ToString() + "\r\n");
            }

            s.Append("------------------------------------------------" + "\r\n");

            Fcdbas.Core.Log.XTrace.Write(s.ToString(), LogPath);
           // Event(s.ToString());
            s.Length = 0;
            
            if (e.Connection != null)
            {
                FileBlock fb = Transfer.Blocks.Find(p => p.ConnectionID == e.Connection.ConnectionId);
                if (fb != null)
                {
                    if (!fb.Complated)
                        fb.StartProcess = false;
                }
                e.Connection.AsClientConnection().BeginReconnect();
            }
          
        }
        
        #endregion

        #endregion

    }
}
